﻿//  Copyright (c) 2011 Ray Liang (http://www.dotnetage.com)
//  Licensed MIT: http://www.opensource.org/licenses/mit-license.php

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DNA.Mvc.Data.Entity;
using System.Linq.Expressions;

namespace DNA.Mvc.Management
{
    public class EntityWebStatRepository : EntityRepositoryBase<LogDB, WebRequestEntry>, IWebStatRepository
    {
        public EntityWebStatRepository(string nameOrConnectionString)
        {
            context = new LogDB(nameOrConnectionString);
            dbSet = this.Context.Set<WebRequestEntry>();
            IsOwnContext = true;
        }

        public EntityWebStatRepository(LogDB dbContext)
        {
            context = dbContext;
            dbSet = this.Context.Set<WebRequestEntry>();
            IsOwnContext = false;
        }

        public IEnumerable<StatEntry> GetIncomingLinks(string website, int maxResult)
        {
            var query = dbSet.Where(w => !string.IsNullOrEmpty(w.UrlRefer));
            if (!string.IsNullOrEmpty(website))
                query = dbSet.Where(w => !string.IsNullOrEmpty(w.UrlRefer) && w.WebSiteName.Equals(website, StringComparison.OrdinalIgnoreCase));

            return query.GroupBy(w => w.UrlRefer, (k, g) => new StatEntry() { Title = k, Count = g.Count() })
                .OrderByDescending(g => g.Count)
                .Take(maxResult);
        }

        public IEnumerable<StatEntry> GetBrowsers(string website)
        {
            var query = dbSet.Where(w => !string.IsNullOrEmpty(w.Browser));
            if (!string.IsNullOrEmpty(website))
                query = dbSet.Where(w => !string.IsNullOrEmpty(w.Browser) && w.WebSiteName.Equals(website, StringComparison.OrdinalIgnoreCase));

            return query.GroupBy(w => w.Browser, (k, g) => new StatEntry() { Title = k, Count = g.Count() })
            .OrderByDescending(g => g.Count);
        }

        public IEnumerable<StatEntry> GetBrowsers(string website, DateTime start, DateTime end)
        {
            var query = dbSet.Where(w => !string.IsNullOrEmpty(w.Browser));
            if (!string.IsNullOrEmpty(website))
                query = dbSet.Where(w => !string.IsNullOrEmpty(w.Browser) && w.WebSiteName.Equals(website, StringComparison.OrdinalIgnoreCase));

            return query.Where(w => w.EventTime >= start && w.EventTime <= end)
                .GroupBy(w => w.Browser, (k, g) => new StatEntry() { Title = k, Count = g.Count() })
                .OrderByDescending(g => g.Count);
        }

        public IEnumerable<StatEntry> GetCrawlers(string website)
        {
            var query = dbSet.Where(w => w.IsCrawler);

            if (!string.IsNullOrEmpty(website))
                query = dbSet.Where(w => w.IsCrawler && w.WebSiteName.Equals(website, StringComparison.OrdinalIgnoreCase));

            return query.GroupBy(w => w.UserAgent, (k, g) => new StatEntry() { Title = k, Count = g.Count() })
                .OrderByDescending(g => g.Count);
        }

        public IEnumerable<StatEntry> GetCrawlers(string website, DateTime start, DateTime end)
        {
            var query = dbSet.Where(w => w.IsCrawler);

            if (!string.IsNullOrEmpty(website))
                query = dbSet.Where(w => w.IsCrawler && w.WebSiteName.Equals(website, StringComparison.OrdinalIgnoreCase));

            return query.Where(w => w.EventTime >= start && w.EventTime <= end)
                .GroupBy(w => w.UserAgent, (k, g) => new StatEntry() { Title = k, Count = g.Count() })
                .OrderByDescending(g => g.Count);
        }

        public IEnumerable<StatEntry> GetVisitors(string website, DateParts datePart = DateParts.Day)
        {
            var query = DbSet.Where(w => !w.IsCrawler);

            if (!string.IsNullOrEmpty(website) && !website.Equals("home"))
                query = query.Where(w => w.WebSiteName.Equals(website, StringComparison.OrdinalIgnoreCase));

            return GetTrafficCore(query, datePart);
        }

        public IEnumerable<StatEntry> GetVisitors(string website, DateTime start, DateTime end, DateParts datePart = DateParts.Day)
        {
            var query = DbSet.Where(w => !w.IsCrawler).Where(w => w.EventTime >= start && w.EventTime <= end);

            if (!string.IsNullOrEmpty(website) && !website.Equals("home"))
                query = query.Where(w => w.WebSiteName.Equals(website, StringComparison.OrdinalIgnoreCase));

            return GetTrafficCore(query, datePart);
        }

        private IEnumerable<StatEntry> GetTrafficCore(IQueryable<WebRequestEntry> dataSource, DateParts part)
        {
            //Step 1 Create temp table
            var localTmp = (from p in dataSource
                            group p by new { p.UserHostName, p.EventTime } into g
                            select new
                            {
                                IP = g.Key.UserHostName,
                                Year = g.Key.EventTime.Year,
                                Month = g.Key.EventTime.Month,
                                Day = g.Key.EventTime.Day
                            })
                      .Distinct()
                      .OrderBy(p => p.Year)
                      .ThenBy(p => p.Month)
                      .ThenBy(p => p.Day)
                      .ToList();

            var getGroupExpr = new Func<DateParts, int, int, int, string>((_dp, y, m, d) =>
            {
                switch (_dp)
                {
                    case DateParts.Year:
                        return y.ToString();
                    case DateParts.Month:
                        return string.Format("{0}/{1}", y, m);
                    default:
                        return string.Format("{0}/{1}/{2}", y, m, d);
                }
            });

            var pvs = (from p in localTmp
                       select new
                       {
                           IP = p.IP,
                           DateString = getGroupExpr(part, p.Year, p.Month, p.Day)
                       });

            var pvGroup = from p in pvs
                          group p by p.DateString into g
                          select new StatEntry()
                          {
                              Count = g.Count(),
                              Title = g.Key
                          };

            return pvGroup.ToList();
        }

        private IEnumerable<StatEntry> GetPageViewsCore(IQueryable<WebRequestEntry> dataSource, DateParts part)
        {
            //Step 1 Create temp table
            var localTmp = (from p in dataSource
                            group p by new { p.UserHostName, p.EventTime } into g
                            select new
                            {
                                IP = g.Key.UserHostName,
                                Year = g.Key.EventTime.Year,
                                Month = g.Key.EventTime.Month,
                                Day = g.Key.EventTime.Day
                            })
                      .OrderBy(p => p.Year)
                      .ThenBy(p => p.Month)
                      .ThenBy(p => p.Day)
                      .ToList();

            var getGroupExpr = new Func<DateParts, int, int, int, string>((_dp, y, m, d) =>
            {
                switch (_dp)
                {
                    case DateParts.Year:
                        return y.ToString();
                    case DateParts.Month:
                        return string.Format("{0}/{1}", y, m);
                    default:
                        return string.Format("{0}/{1}/{2}", y, m, d);
                }
            });

            var pvs = (from p in localTmp
                       select new
                       {
                           IP = p.IP,
                           DateString = getGroupExpr(part, p.Year, p.Month, p.Day)
                       });

            var pvGroup = from p in pvs
                          group p by p.DateString into g
                          select new StatEntry()
                          {
                              Count = g.Count(),
                              Title = g.Key
                          };

            return pvGroup.ToList();
        }

        public IEnumerable<StatEntry> GetPageViews(string website, DateParts datePart = DateParts.Day)
        {
            var query = DbSet.Where(w => !w.IsCrawler);

            if (!string.IsNullOrEmpty(website))
                query = query.Where(w => w.WebSiteName.Equals(website, StringComparison.OrdinalIgnoreCase));

            return GetPageViewsCore(query, datePart);
        }

        public IEnumerable<StatEntry> GetPageViews(string website, DateTime start, DateTime end, DateParts datePart = DateParts.Day)
        {
            var query = DbSet.Where(w => !w.IsCrawler).Where(w => w.EventTime >= start && w.EventTime <= end);

            if (!string.IsNullOrEmpty(website))
                query = query.Where(w => w.WebSiteName.Equals(website, StringComparison.OrdinalIgnoreCase));

            return GetPageViewsCore(query, datePart);
        }

        public void Clear()
        {
            Context.Database.ExecuteSqlCommand("delete WebRequestEntries");
        }

        public WebStatistics GetStatistics(string website, int interval)
        {
            var start = DateTime.Today;
            var end = start.AddHours(24);
            var visitors = GetVisitors(website, start, end).FirstOrDefault();
            var pageViews = GetPageViews(website, start, end).FirstOrDefault();

            var passed= DateTime.UtcNow.AddMinutes(0 - interval);
            var now= DateTime.UtcNow;

            var onlineusers = DbSet.Where(l => !l.IsCrawler && l.EventTime >= passed && l.EventTime <= now).ToList();
            var registers = onlineusers.Where(o => !o.IsAnonymous).Select(l => l.UserName).Distinct().ToArray();
            var anonymous = onlineusers.Where(o => o.IsAnonymous).Select(l => l.UserName).Distinct().ToArray();


            var stat = new WebStatistics()
            {
                Visitors = visitors == null ? 0 : visitors.Count,
                PageViews = pageViews == null ? 0 : pageViews.Count,
                OnlineRegisters = registers,
                OnlineGuests = anonymous
            };
            return stat;
        }

    }

    public enum DateParts
    {
        Year = 0,
        Month = 1,
        Day = 2
    }
}
